package com.support.mvc.entity;

import com.alibaba.fastjson.annotation.JSONField;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.apache.commons.lang3.StringUtils;

import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * 用户信息抽象接口， 用于 Controller 层泛型获取用户信息
 *
 * @author 谢长春
 */
public interface IOpenUser {

    /**
     * IOpenUser 专用方法
     * 判断用户是否已登录
     *
     * @return boolean true：是，false：否
     */
    @ApiModelProperty(hidden = true)
    @JSONField(serialize = false, deserialize = false)
    default boolean isUserSessionExist() {
        return false;
    }

    /**
     * IOpenUser 专用方法
     * 判断用户是否已登录，已登录则执行 userConsumer
     *
     * @param userConsumer {@link Consumer<IUser >}
     * @return {@link IUser}
     */
    default IUser ifUserSessionExist(final Consumer<IUser> userConsumer) {
        return IUser.EMPTY;
    }

    /**
     * IOpenUser 专用方法
     * 使用 token 填充用户
     *
     * @return {@link IOpenUser}
     */
    default IUser fillUserByToken() {
        return IUser.EMPTY;
    }

    @Getter
    @Setter
    @RequiredArgsConstructor
    @ToString(exclude = {"token", "loadUserByTokenFunction"})
    class OpenUser implements IUser {
        /**
         * 非 session 模式时，获取请求头 token 存储到该属性
         */
        @JSONField(serialize = false, deserialize = false)
        private final String token;
        /**
         * token 换用户信息
         */
        @JSONField(serialize = false, deserialize = false)
        private final Function<String, IUser> loadUserByTokenFunction;

        /**
         * 客户端指纹
         */
        private String fingerprint;

        /**
         * 用户id
         */
        private Long id;

        /**
         * 用户登录名
         */
        private String username;

        /**
         * 用户昵称
         */
        private String nickname;

        /**
         * 用户手机号
         */
        private String phone;

        /**
         * IOpenUser 专用方法
         * 判断用户是否已登录
         *
         * @return boolean true：是，false：否
         */
        @Override
        @JSONField(serialize = false, deserialize = false)
        public boolean isUserSessionExist() {
            return Optional.ofNullable(getId()).filter(val -> val > 0).isPresent();
        }

        @Override
        public IUser ifUserSessionExist(Consumer<IUser> userConsumer) {
            if (Objects.nonNull(userConsumer) && isUserSessionExist()) {
                userConsumer.accept(this);
            }
            return this;
        }

        /**
         * 使用 token 填充用户
         *
         * @return {@link IOpenUser}
         */
        @Override
        public IUser fillUserByToken() {
            if (!isUserSessionExist()) {
//            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
                try {
                    if (StringUtils.isNotBlank(this.token)) {
                        final IUser user = loadUserByTokenFunction.apply(token);
                        this.id = user.getId();
                        this.username = user.getUsername();
                        this.nickname = user.getNickname();
                        this.phone = user.getPhone();
                    }
                } catch (Exception e) {
                    // 异常不用管，表示用户未登录或登陆超时，开放接口依然允许访问
                }
            }
            return this;
        }
    }

//    /**
//     * 开放接口用户用户认证
//     */
//    @EqualsAndHashCode(callSuper = true)
//    class IOpenUserAuthenticationToken extends AnonymousAuthenticationToken {
//        private static final long serialVersionUID = 1L;
//        private final Object credentials;
//        private final Object principal;
//
//        public IOpenUserAuthenticationToken(final IOpenUser principal) {
//            // 开放接口强制使用游客权限，避免开放接口注入权限，导致越权访问
//            super(AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
//            this.principal = principal;
//            this.credentials = null;
//            setAuthenticated(true);
//        }
//
//        public Object getCredentials() {
//            return this.credentials;
//        }
//
//        public Object getPrincipal() {
//            return this.principal;
//        }
//    }
}
